package jvm.多线程交替打印方案;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @description: 多线程交替打印方案
 * @author: 小白白
 * @create: 2021-12-27
 **/

public class Main {

    private static class LockList {

        private volatile boolean firstGo = false;

        // 一个Lock.lock只能被一个人锁着, 除非另一个人调用await或者unlock
        private Lock lock = new ReentrantLock();
        private List<Condition> list = new ArrayList<>();
        private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

        public synchronized void addThread() {
            Condition condition = this.lock.newCondition();
            this.list.add(condition);
            this.threadLocal.set(this.list.size() - 1);
            /**
             * 这里想加全局lock? 暂时不可行, 因为必须通过的线程执行await后, 这里的lock才可以执行, 而内部线程一旦await,
             * 那么它的signal已经执行过了, 可以寻找其他方法,但是比较麻烦了
             */
        }

        // 阻塞后前进
        public void lockAndForward() {

            Condition condition = this.list.get(this.threadLocal.get());

            // 等待前节点唤醒
            try {
                if (this.firstGo) {
                    this.lock.lock();
                    condition.await();
                    this.lock.unlock();
                } else {
                    this.firstGo = true;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 当前流程执行完毕,唤醒下节点
        public void awakenNextLock() {
            Condition condition = this.list.get((this.threadLocal.get() + 1) % this.list.size());
            this.lock.lock();
            condition.signal();
            this.lock.unlock();
        }

    }

    private static volatile int i = 0;

    public static void main(String[] args) throws InterruptedException {

        LockList lockList = new LockList();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                lockList.addThread();
                while (true) {
                    lockList.lockAndForward();
                    System.out.println(Thread.currentThread().getName() + ":" + (i++));
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lockList.awakenNextLock();
                }
            }
        };

        Thread thread1 = new Thread(runnable, "1");
        Thread thread2 = new Thread(runnable, "2");
        Thread thread3 = new Thread(runnable, "3");
        Thread thread4 = new Thread(runnable, "4");
        Thread thread5 = new Thread(runnable, "5");

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();

        for (int j = 6; j <= 20; j++) {
            new Thread(runnable, "" + j).start();
        }

    }

}
